
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include <libxml/encoding.h>
#include <libxml/xmlwriter.h>
#include <libxml/parser.h>
#include <pwd.h>
#include <sys/types.h>
#include <string.h>
#include "database.h"
/*
<dev class="software">
  <dev class="device" id="1" describe="left_wheel">
    <dev class="interface" id="1" type="dc">
        <dev class="point" id="1" type="point" pin="3" pwmchip="2" pwm="3"></dev>
        <dev class="point" id="2" type="point" pin="3" pwmchip="2" pwm="3"></dev>
    </dev>
  </dev>
  <dev class="device" id="2" describe="right_wheel">
    <dev class="interface" id="2" type="dc">
        <dev class="point" id="3" type="point" pin="3" pwmchip="2" pwm="3"></dev>
        <dev class="point" id="4" type="point" pin="3" pwmchip="2" pwm="3"></dev>
    </dev>
  </dev>
</dev>
 */
#include "sqlite3.h"
static int database_db2xml_callback(void *ptr, int n_column,
    char **column_value, char **column_name)
{
  int i = 0;
  xmlNodePtr root = ptr, node = NULL;

  if (n_column == 0) {
    printf("sql: column is 0\n");
    return true;
  }
  node = xmlNewNode(NULL, BAD_CAST "dev");
  for(i = 0; i < n_column; i++) {
    xmlNewProp(node, BAD_CAST column_name[i],
        BAD_CAST column_value[i]);
  }
  xmlAddChild(root, node);
  return 0;
}
xmlNodePtr database_sql2xml(void *db, const char *statement,
    xmlNodePtr parent)
{
  int rc;
  char *errmsg = NULL;

  rc = sqlite3_exec((sqlite3 *)db, statement, database_db2xml_callback,
      parent, &errmsg);
  if (rc != SQLITE_OK) {
    fprintf(stderr, "Error: L%d f-%s %s:%s:%d\n", __LINE__, __func__,
        statement, errmsg, rc);
    xmlNewProp(parent, BAD_CAST "error", BAD_CAST errmsg);
  }
  return parent;
}
static char *xml_node_to_str(void *node)
{
  int rc;
  char *res = NULL;
  xmlNodePtr root = node;
  xmlBufferPtr xmlbuf = NULL;

  if (!node) {
    printf("[WARN] %s %d no node\n", __func__, __LINE__);
    return NULL;
  }
  xmlbuf = xmlBufferCreate();
  rc = xmlNodeDump(xmlbuf, NULL, root, 1, 1);
  if (rc == -1) {
    printf("[WARN] %s %d xmlnodedump error\n", __func__, __LINE__);
  } else {
    res = strdup((char *)xmlbuf->content);
  }
  if (xmlbuf)
    xmlBufferFree(xmlbuf);
  xmlFreeNode(root);
  return res;
}
char *database_sql2str(void *db, const char *statement)
{
  xmlNodePtr all;
  char *res = NULL;

  all = xmlNewNode(NULL, BAD_CAST "dev");
  database_sql2xml(db, statement, all);
  res = xml_node_to_str(all);
  return res;
}
static int sql_count_callback(void *ptr,
    int n_column, char **column_value, char **column_name)
{
  int rc = 0, *sum = NULL;

  printf("[DEBUG] L%d f-%s\n", __LINE__, __func__);
  if (n_column == 0) {
    printf("sql: column is 0\n");
    rc = 1;
    goto err;
  }
  if (ptr) {
    sum = ptr;
    *sum = *sum + 1;
  } else {
    printf("[ERROR]NO PTR\n");
  }
err:
      return rc;
}

int database_sql2int(void *sql, const char *statement)
{
  int rc = 0, count = 0;
  char *errmsg = NULL;

  printf("[DEBUG] L%d f-%s statement:%s\n",
      __LINE__, __func__, statement);
  rc = sqlite3_exec(sql, statement, sql_count_callback,
      &count, &errmsg);
  if (rc != SQLITE_OK) {
    printf("[WARN] L%d f-%s rc:%d %s", __LINE__, __func__, rc, errmsg);
    rc = -1;
    goto err;
  }
  rc = count;
err:
  return rc;
}
int database_exist(void *db,
    const char *table, const char *field, const char *value)
{
  char statement[1024] = {0};
  int rc = 0;

  snprintf(statement, sizeof(statement),
      "select 1 from '%s' where %s='%s' limit 1;",
      table, field, value);
  rc = database_sql2int(db, statement);

  return rc;
}
int database_last_insert_rowid(void *db)
{
  return sqlite3_last_insert_rowid(db);
}

struct database_struct *database__init(int argc, const char*const *argv)
{
  struct database_struct *self = NULL;

  xmlDocPtr doc;
  xmlNodePtr root_node, now;
  xmlChar *xml_char = NULL;
  const char *filename;
  if ((argc == 3) && !strcmp(argv[1], "-c")) {
    filename = argv[2];
  } else {
    printf("Usage: %s [OPTION]... [FILE]...\n\
%s\n\
-c\tconfig file\n\
Examples:\n\
\t%s -c config.xml\n", SOFTWARE_NAME, SOFTWARE_NAME, SOFTWARE_NAME);
    exit(1);
  }
  if (access(filename, F_OK) == 0) {
    doc = xmlParseFile(filename);
    if (doc) {
      printf("[DEBUG]xml_driver__file:%s\n", filename);
    } else {
      printf("[ERROR] xml_driver__file:%s xmlParseFile error\n",
          filename);
      exit(1);
    }
  } else {
    printf("[----ERROR] no %s\n", filename);
    exit(1);
  }
  self = calloc(1, sizeof(*self));
  root_node = xmlDocGetRootElement(doc);
  now = xmlFirstElementChild(root_node);
  now = xmlFirstElementChild(now);
  xml_char = xmlGetProp(now, BAD_CAST "test");
  if (xml_char) {
    self->test = atoi((char *)xml_char);
    xmlFree(xml_char);
  }
  xml_char = xmlGetProp(now, BAD_CAST "databaseDbName");
  if (xml_char) {
    self->dbName = strdup((char *)xml_char);
    xmlFree(xml_char);
  }
  int rc;
  sqlite3 *db;
  const char *dbName = self->dbName;
  rc = sqlite3_open(dbName, &db);
  if (rc != SQLITE_OK) {
    printf("[ERROR] sqlite3 open %s and exit\n", dbName);
    exit(1);
  } else {
    printf("[DEBUG] sqlite3 open %s succeed\n", dbName);
  }
  self->db = db;
  return self;
}
xmlNodePtr database__sqlExecToXml(void *db, const char *statement,
    xmlNodePtr parent)
{
  int rc;
  char *errmsg = NULL;

  rc = sqlite3_exec((sqlite3 *)db, statement, database_db2xml_callback,
      parent, &errmsg);
  if (rc != SQLITE_OK) {
    fprintf(stderr, "Error: L%d f-%s %s:%s:%d\n", __LINE__, __func__,
        statement, errmsg, rc);
    xmlNewProp(parent, BAD_CAST "error", BAD_CAST errmsg);
  }
  return parent;
}
char *database__sqlMemExecWithDbName(void *db, const char *msg, int len,
    const char *dbName)
{
  xmlNodePtr all;
  char *res = NULL, statement[1024] = {0};

  memcpy(statement, msg, len);
  all = xmlNewNode(NULL, BAD_CAST "dev");
  xmlNewProp(all, BAD_CAST "dbName", BAD_CAST dbName);
  database_sql2xml(db, statement, all);
  res = xml_node_to_str(all);
  return res;
}
char *database__sqlExecWithDbName(void *db, const char *msg,
    const char *dbName)
{
  xmlNodePtr all;
  char *res = NULL;

  all = xmlNewNode(NULL, BAD_CAST "dev");
  xmlNewProp(all, BAD_CAST "dbName", BAD_CAST dbName);
  database__sqlExecToXml(db, msg, all);
  res = xml_node_to_str(all);
  return res;
}
char *database__xmlSqlExecWithDbName(void *db, const char *msg,
    const char *dbName)
{
  if (!msg) {
    printf("[ERROR] %s no sql\n", __func__);
    return NULL;
  }
  xmlDocPtr doc;
  xmlNodePtr root, child;
  xmlChar *xml_char = NULL;
  char *res = NULL;

  doc = xmlParseDoc(BAD_CAST msg);
  root = xmlDocGetRootElement(doc);
  child = xmlFirstElementChild(root);
  xml_char = xmlGetProp(child, BAD_CAST "sql");
  if (xml_char) {
    res = database__sqlExecWithDbName(db, (char *)xml_char, dbName);
    xmlFree(xml_char);
  }
  xmlFreeDoc(doc);
  return res; 
}
